<?php
/**
 * jishupu-cms
 * @Author:技术铺<jishupu@qq.com>
 * @Date: 2021/11/11
 */

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;

/**
 * 带场景的表单请求验证基类
 * Class BaseRequest
 * @package App\Http\Requests
 */
class BaseRequest extends FormRequest
{
    /**
     * 多场景设置
     * @var array
     * 例：
     *   $scenes = [
     *     'store'   => ['title','content'],
     *     'update'  => ['id','title','content'],
     *     'destroy' => ['id'],
     *   ]
     */
    protected $scenes = [];

    /**
     * 注入表单请求验证时自动校验
     *
     * @var bool
     */
    public $autoValidate = false;

    /**
     * 当前场景
     * @var string
     */
    protected $currentScene = '';

    /**
     * 用户是否拥有操作权限：true是，false否
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * 关闭自动验证
     *
     * @throws ValidationException
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function validateResolved()
    {
        if ($this->autoValidate) {
            $this->handleValidate();
        }
    }

    /**
     * 获取场景验证规则
     * @return array|mixed
     */
    public function getRules()
    {
        $rules = $this->container->call([$this, 'rules']);
        $sceneRules = [];
        if ($this->currentScene && isset($this->scenes[$this->currentScene])) {
            $sceneFields = is_array($this->scenes[$this->currentScene])
                ? $this->scenes[$this->currentScene] : explode(',', $this->scenes[$this->currentScene]);
            foreach ($sceneFields as $field) {
                if (array_key_exists($field, $rules)) {
                    $sceneRules[$field] = $rules[$field];
                }
            }
            return $sceneRules;
        }
        return $rules;
    }

    /**
     * 覆盖验证器
     * @param $factory
     * @return mixed
     */
    public function validator($factory)
    {
        return $factory->make(
            $this->validationData(), $this->getRules(),
            $this->messages(), $this->attributes()
        );
    }

    /**
     * 手动调用的验证方法
     * @param string $scene
     * @return array|void
     * @throws \Illuminate\Auth\Access\AuthorizationException
     * @throws \Illuminate\Validation\ValidationException
     */
    public function validate($scene = '')
    {
        if (!empty($scene)) {
            $this->currentScene = $scene;
        }
        $this->handleValidate();
        return $this->validated();
    }

    /**
     * 验证失败处理
     *
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
     * @return void
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    protected function failedValidation(Validator $validator)
    {
        if(!$this->expectsJson()) {
            // web站点验证失败处理方式
            redirect($this->getRedirectUrl())
                ->withErrors($validator)
                ->withInput();
        } else {
            // 原有返回方式，若api接口场景在Exceptions/Handler.php中捕获处理
            throw (new ValidationException($validator))
                ->errorBag($this->errorBag)
                ->redirectTo($this->getRedirectUrl());
        }
    }

    /**
     * 实际执行的验证方法
     * @throws \Illuminate\Auth\Access\AuthorizationException
     * @throws \Illuminate\Validation\ValidationException
     */
    protected function handleValidate()
    {
        $this->prepareForValidation();
        if (!$this->passesAuthorization()) {
            $this->failedAuthorization();
        }
        $instance = $this->getValidatorInstance();
        if ($instance->fails()) {
            $this->failedValidation($instance);
        }
        $this->passedValidation();
    }
}
